package main

import (
	"archive/zip"
	"bufio"
	"bytes"
	"encoding/csv"
	"encoding/json"
	"fmt"
	"github.com/RoaringBitmap/roaring/roaring64"
	"github.com/xuri/excelize/v2"
	"go.manyji.com/lib/config"
	"go.manyji.com/lib/util/convert"
	"go.manyji.com/lib/util/errors"
	"html/template"
	"io"
	"mime/multipart"
	"net/http"
	"net/textproto"
	"os"
	"path"
	"path/filepath"
	"reflect"
	"runtime"
	"sieve_agent_server/lib/excel"
	"strconv"
	"strings"
	"time"
)

var (
	timeFormat  = "2006-01-02 15:04:05"
	FileStorage = *config.String("file.storage", fmt.Sprintf("/data/%v", filepath.Base(os.Args[0])), "file storage dir prefix")
)

/*
数据吧返回的筛性别格式的Sex字段描述：

	unkonw-无法识别头像
	no-hedeless-没有头像
	male-男
	female-女

	status 理解为个性签名就行
	valid 理解为有效，个人觉得没有实际用途，可以忽略
*/
type SieveSexExportType struct {
	Mobile   string `xlsx:"mobile"`
	Sex      string `xlsx:"sex"`
	Age      string `xlsx:"age"`
	Picture  string `xlsx:"pic"`
	UserName string `xlsx:"userName"`
	UID      string `xlsx:"uid"`
	//Valid    string `xlsx:"valid"`
	NickName string `xlsx:"nickname"`
	//Status   string `xlsx:"-"`
}

type OperatorSieveSexExportType struct {
	Number         string `xlsx:"number"`
	CarrierId      string `xlsx:"carrierId"`
	CarrierName    string `xlsx:"carrierName"`
	CarrierMnc     string `xlsx:"carrierMnc"`
	Validity       string `xlsx:"validity"`
	NumberType     string `xlsx:"numberType"`
	CountryCode    string `xlsx:"countryCode"`
	CountryName    string `xlsx:"countryName"`
	LastKnownEvent string `xlsx:"lastKnownEvent"`
	DisconnectDate string `xlsx:"disconnectDate"`
	ProvinceCode   string `xlsx:"provinceCode"`
}

type GeneralSieveSexExportType map[string]string

func Format(sec int64, layout ...string) string {

	if len(layout) > 0 {
		timeFormat = layout[0]
	}

	return time.Unix(sec, 0).Format(timeFormat)
}

// 读取结构体的 tag,返回切片
func Struct2Slice(obj interface{}, tagName, prefix string, ignores ...string) []string {
	t := reflect.TypeOf(obj)

	var data []string
	for i := 0; i < t.NumField(); i++ {
		keyName := t.Field(i).Tag.Get(tagName)
		if keyName != "" && keyName != "-" {
			for _, v := range ignores {
				if v == keyName {
					continue
				}
			}
			if prefix != "" {
				data = append(data, fmt.Sprintf("%v.%v as %v", prefix, keyName, keyName))
			} else {
				data = append(data, fmt.Sprintf("%v", keyName))
			}
		}
	}
	return data
}

// PathExist 判断目录是否存在
func PathExist(addr string) bool {
	s, err := os.Stat(addr)
	if err != nil {
		return false
	}
	return s.IsDir()
}

func Struct2Map(obj interface{}, tagName string) map[string]interface{} {
	t := reflect.TypeOf(obj)
	v := reflect.ValueOf(obj)

	if t.Kind() == reflect.Ptr {
		t = t.Elem()
		v = v.Elem()
	}

	var data = make(map[string]interface{})
	for i := 0; i < t.NumField(); i++ {
		keyName := t.Field(i).Tag.Get(tagName)
		if keyName != "" && keyName != "-" {
			data[keyName] = v.Field(i).Interface()
		}
	}
	return data
}

func Decimal(num float64, args ...interface{}) float64 {

	var bit = 5
	if len(args) == 1 {
		bit = args[0].(int)
	}

	num, _ = strconv.ParseFloat(fmt.Sprintf(fmt.Sprintf("%%.%vf", bit), num), 64)
	return num
}

func GetFilePath(d string, args ...interface{}) string {

	var isPath bool
	if len(args) == 1 {
		isPath = args[0].(bool)
	}

	dir, fileName := path.Dir(d), filepath.Base(d)
	if runtime.GOOS == "windows" {

		g, _ := os.Getwd()
		dir = path.Clean(g + string(os.PathSeparator) + dir)
	}

	_ = os.MkdirAll(dir, os.ModePerm)
	if isPath {
		return dir
	}
	return path.Clean(dir + string(os.PathSeparator) + fileName)
}

// UploadSieve
// 1. 上传料子
/*
 请求：
	{
		“file”: file, //上传的料子 txt 文件
		“taskNumName”: //任务名
		“country”: //国家名 例如 IN,ID “hour”：时间 //默认 24 小时，可以不填 整数
		“activeType”：//活跃类型需加入这个参数，0--频繁上线，1---最近上线，2--近期上线
	}
 返回
	{
		"returnCode":200," 返回状态码，200 正常
		"returnMsg":"wa:nums:info:xa_01_01_in, 返回信息
		"data":"0811-filter-xa_01_01_in-1" 返回的 taskId
	}
*/

func CreateFormFile(w *multipart.Writer, fieldname, filename string) (io.Writer, error) {
	h := make(textproto.MIMEHeader)
	h.Set("Content-Disposition",
		fmt.Sprintf(`form-data; name="%s"; filename="%s"`,
			fieldname, filename))
	h.Set("Content-Type", "text/plain")
	return w.CreatePart(h)
}

func UploadSieve(filePath, url, act, operator, credentials, taskNumName, country string, activeType int) (map[string]interface{}, error) {

	url = fmt.Sprintf("%v?operator=%v&act=%v&credentials=%v", url, operator, act, credentials)

	if activeType != -1 {
		url += fmt.Sprintf("&activeType=%v", activeType)
	}

	method := "POST"

	payload := &bytes.Buffer{}
	writer := multipart.NewWriter(payload)
	file, errFile1 := os.Open(filePath)
	defer file.Close()
	baseFile := filepath.Base(filePath)
	ss := strings.Split(baseFile, ".")
	baseFile = ss[0] + "_" + convert.ToString(time.Now().UnixNano()) + "." + ss[len(ss)-1]

	part1, errFile1 := CreateFormFile(writer, "file", baseFile)
	_, errFile1 = io.Copy(part1, file)
	if errFile1 != nil {
		fmt.Println(errFile1)
		return nil, errFile1
	}
	_ = writer.WriteField("taskNumName", taskNumName)
	_ = writer.WriteField("country", country)
	err := writer.Close()
	if err != nil {
		fmt.Println(err)
		return nil, err
	}

	client := &http.Client{}
	req, err := http.NewRequest(method, url, payload)

	if err != nil {
		fmt.Println(err)
		return nil, err
	}
	req.Header.Set("Content-Type", writer.FormDataContentType())
	res, err := client.Do(req)
	if err != nil {
		fmt.Println(err)
		return nil, err
	}
	defer res.Body.Close()

	body, err := io.ReadAll(res.Body)
	if err != nil {
		fmt.Println(err)
		return nil, err
	}
	fmt.Println(string(body))

	respMap := make(map[string]interface{})
	err = json.Unmarshal(body, &respMap)
	if err != nil {
		return nil, err
	}

	return respMap, nil
}

// QuerySieveStatus
// 2. 状态查询示例
/*
 请求：
	{
		“operator”：“userInfo”
		“act”：shitou, //帐号
		“credentials”: 728ac5836c5da89bdfb214cd5934e68f , //token 验证
	}
 返回
	{
		"returnCode":200, 返回 code
		"returnMsg":"success", 返回信息
		"data":{
			"state" : 状态
			"count":任务书
			"completeCount":完成数
			"okcount":有效数
		}
	}
*/
func QuerySieveStatus(taskId, url, act, credentials string) (map[string]interface{}, error) {

	url = fmt.Sprintf("%v?operator=search&taskId=%v&act=%v&credentials=%v", url, taskId, act, credentials)
	method := "GET"

	client := &http.Client{}
	req, err := http.NewRequest(method, url, nil)
	if err != nil {
		fmt.Println(err)
		return nil, err
	}

	res, err := client.Do(req)
	if err != nil {
		fmt.Println(err)
		return nil, err
	}
	defer res.Body.Close()

	body, err := io.ReadAll(res.Body)
	if err != nil {
		fmt.Println(err)
		return nil, err
	}
	fmt.Println(string(body))

	respMap := make(map[string]interface{})
	err = json.Unmarshal(body, &respMap)
	if err != nil {
		return nil, err
	}

	return respMap, nil
}

func DownSieveFile(taskId, url, act, credentials string, outFile string, fCallBack func(number string)) ([]string, error) {

	url = fmt.Sprintf("%v?operator=down&taskId=%v&act=%v&credentials=%v", url, taskId, act, credentials)
	method := "GET"

	client := &http.Client{}
	req, err := http.NewRequest(method, url, nil)

	if err != nil {
		fmt.Println(err)
		return nil, err
	}
	res, err := client.Do(req)
	if err != nil {
		fmt.Println(err)
		return nil, err
	}
	defer res.Body.Close()

	out, err := os.Create(outFile)
	if err != nil {
		return nil, err
	}

	defer out.Close()

	bufWriter := bufio.NewWriter(out)
	var (
		phones []string
		reader = bufio.NewReader(res.Body)
	)

	// 按行处理txt
	for {
		line, _, err := reader.ReadLine()
		if err == io.EOF {
			break
		}

		// 1. 数据吧返回的ws 的筛有效的格式是这样的 xxx:xxx:xxx:xxx
		d := strings.Split(string(line), ":")[0]

		// 2. 数据吧返回的fb 的筛有效个格式 xxx_x
		d = strings.Split(string(d), "_")[0]

		fCallBack(d)
		_, _ = bufWriter.WriteString(fmt.Sprintf("%v%c%c", d, 0x0D, 0x0A))
	}

	_ = bufWriter.Flush()

	return phones, nil
}

func DownSexSieveFile(taskId, url, act, credentials string, outFile string) ([]string, error) {

	url = fmt.Sprintf("%v?operator=genderDown&taskId=%v&act=%v&credentials=%v", url, taskId, act, credentials)
	method := "GET"

	client := &http.Client{}
	req, err := http.NewRequest(method, url, nil)

	if err != nil {
		fmt.Println(err)
		return nil, err
	}
	res, err := client.Do(req)
	if err != nil {
		fmt.Println(err)
		return nil, err
	}
	defer res.Body.Close()

	out, err := os.Create(outFile)
	if err != nil {
		return nil, err
	}

	_, err = io.Copy(out, res.Body)

	return nil, err
}

/*
ty 指定分割的小文件的文件格式
1: txt
2: csv
*/
func TxtFileSplitToTxt(fileName string, npf int) ([]string, error) {

	f, err := os.Open(fileName)
	if err != nil {
		return nil, err
	}
	defer f.Close()
	reader := bufio.NewReader(f)

	var (
		baseFile = filepath.Base(fileName)
		index    = 1
		count    = 1
		files    []string
		ss       = strings.Split(baseFile, ".")
		ff       = filepath.Join(filepath.Dir(fileName), fmt.Sprintf("%v_%v", ss[0], index))
	)

	if len(ss) == 2 {
		ff = ff + fmt.Sprintf(".%v", ss[len(ss)-1])
	}

	fWriter, err := os.Create(ff)
	if err != nil {
		return nil, err
	}
	rWriter := bufio.NewWriter(fWriter)

	var hasExtraFile = true
	for {
		line, _, err := reader.ReadLine()
		if err == io.EOF {
			break
		}

		_, _ = rWriter.WriteString(fmt.Sprintf("%v%c%c", string(line), 0x0D, 0x0A))
		if count >= npf && npf != 0 {
			files = append(files, ff)
			_ = rWriter.Flush()
			count = 1
			_ = fWriter.Close()

			if reader.Buffered() <= 0 {
				hasExtraFile = false
				break
			}

			index = index + 1
			//ff = fmt.Sprintf("%v_%v", ss[0], index)
			ff = filepath.Join(filepath.Dir(fileName), fmt.Sprintf("%v_%v", ss[0], index))
			if len(ss) == 2 {
				ff = ff + fmt.Sprintf(".%v", ss[len(ss)-1])
			}
			fWriter, err = os.Create(ff)
			if err != nil {
				fmt.Println(err)
			}
			rWriter = bufio.NewWriter(fWriter)
			continue
		}
		count = count + 1
	}

	if hasExtraFile {
		files = append(files, ff)
		_ = rWriter.Flush()
		_ = fWriter.Close()
	}
	return files, nil
}

func TxtFileSplitToCSV(fileName string, npf int) ([]string, error) {

	f, err := os.Open(fileName)
	if err != nil {
		return nil, err
	}
	defer f.Close()
	reader := bufio.NewReader(f)

	var (
		baseFile = filepath.Base(fileName)
		index    = 1
		count    = 1
		files    []string
		ss       = strings.Split(baseFile, ".")
		ff       = filepath.Join(filepath.Dir(fileName), fmt.Sprintf("%v_%v.csv", ss[0], index))
	)

	fWriter, err := os.Create(ff)
	if err != nil {
		return nil, err
	}
	rWriter := csv.NewWriter(fWriter)

	var hasExtraFile = true
	for {
		line, _, err := reader.ReadLine()
		if err == io.EOF {
			break
		}

		_ = rWriter.Write([]string{fmt.Sprintf("\t%v", string(line))})
		if count >= npf && npf != 0 {
			files = append(files, ff)
			rWriter.Flush()
			count = 1
			_ = fWriter.Close()

			if reader.Buffered() <= 0 {
				hasExtraFile = false
				break
			}

			index = index + 1
			ff = filepath.Join(filepath.Dir(fileName), fmt.Sprintf("%v_%v.csv", ss[0], index))

			fWriter, err = os.Create(ff)
			if err != nil {
				fmt.Println(err)
			}
			rWriter = csv.NewWriter(fWriter)
			continue
		}
		count = count + 1
	}
	if hasExtraFile {
		files = append(files, ff)
		rWriter.Flush()
		_ = fWriter.Close()
	}
	return files, nil
}

func SexExcelFileSplitToExcel(fileName string, npf int, args ...interface{}) ([]string, error) {

	var (
		deDupSieve  []SieveSexExportType
		deDupSieveR []SieveSexExportType
		exportKind  int
		ageMap      = make(map[string]struct{})
	)

	if len(args) == 1 {
		exportKind = args[0].(int)
	}
	if len(args) == 2 {
		exportKind = args[0].(int)

		if args[1].(string) != "" {
			s := strings.Split(args[1].(string), "-")
			if len(s) == 1 {
				ageMap[s[0]] = struct{}{}
			}
			if len(s) == 2 {
				for i := convert.Atoi(s[0]); i <= convert.Atoi(s[1]); i++ {
					ageMap[strconv.Itoa(i)] = struct{}{}
				}
			}
		}
	}

	if err := (excel.Sheet{Filename: fileName, Sheet: "Sheet1"}).Scan(&deDupSieve); err != nil {
		return nil, err
	}

	var (
		baseFile = filepath.Base(fileName)
		index    = 1
		count    = 1
		files    []string
		ss       = strings.Split(baseFile, ".")
		ff       = filepath.Join(filepath.Dir(fileName), fmt.Sprintf("%v_%v.xlsx", ss[0], index))
	)

	for i, v := range deDupSieve {

		if exportKind == 1 && v.Sex != "male" {
			continue
		}

		if exportKind == 2 && v.Sex != "female" {
			continue
		}

		if exportKind == 4 && (v.Sex != "female" && v.Sex != "male") {
			continue
		}

		if _, ok := ageMap[v.Age]; !ok && len(ageMap) > 0 && (exportKind == 1 || exportKind == 2 || exportKind == 4) {
			continue
		}

		deDupSieveR = append(deDupSieveR, v)
		if count >= npf && npf != 0 {
			files = append(files, ff)
			count = 1
			buff, _ := (&excel.Sheet{Sheet: "Sheet1"}).Export(&deDupSieveR)
			_ = os.WriteFile(ff, buff.Bytes(), 0644)
			deDupSieveR = deDupSieveR[:0]
			if i+1 >= len(deDupSieve) {
				break
			}
			index = index + 1
			ff = filepath.Join(filepath.Dir(fileName), fmt.Sprintf("%v_%v.xlsx", ss[0], index))
			continue
		}
		count = count + 1
	}

	if len(deDupSieveR) > 0 {
		buff, _ := (&excel.Sheet{Sheet: "Sheet1"}).Export(&deDupSieveR)
		_ = os.WriteFile(ff, buff.Bytes(), 0644)
		files = append(files, ff)
	}
	return files, nil
}

// 运营商数据格式导出
func OperatorExcelFileSplitToExcel(fileName string, npf int) ([]string, error) {

	var (
		deDupSieve  []OperatorSieveSexExportType
		deDupSieveR []OperatorSieveSexExportType
	)

	if err := (excel.Sheet{Filename: fileName, Sheet: "Sheet1"}).Scan(&deDupSieve); err != nil {
		return nil, err
	}

	var (
		baseFile = filepath.Base(fileName)
		index    = 1
		count    = 1
		files    []string
		ss       = strings.Split(baseFile, ".")
		ff       = filepath.Join(filepath.Dir(fileName), fmt.Sprintf("%v_%v.xlsx", ss[0], index))
	)

	for i, v := range deDupSieve {

		deDupSieveR = append(deDupSieveR, v)
		if count >= npf && npf != 0 {
			files = append(files, ff)
			count = 1
			buff, _ := (&excel.Sheet{Sheet: "Sheet1"}).Export(&deDupSieveR)
			_ = os.WriteFile(ff, buff.Bytes(), 0644)
			deDupSieveR = deDupSieveR[:0]
			if i+1 >= len(deDupSieve) {
				break
			}
			index = index + 1
			ff = filepath.Join(filepath.Dir(fileName), fmt.Sprintf("%v_%v.xlsx", ss[0], index))
			continue
		}
		count = count + 1
	}

	if len(deDupSieveR) > 0 {
		buff, _ := (&excel.Sheet{Sheet: "Sheet1"}).Export(&deDupSieveR)
		_ = os.WriteFile(ff, buff.Bytes(), 0644)
		files = append(files, ff)
	}
	return files, nil
}

// 运营商数据格式导出
func OperatorExcelFileSplitToTxt(fileName string, npf int) ([]string, error) {

	var (
		deDupSieve  []OperatorSieveSexExportType
		deDupSieveR []string
	)

	if err := (excel.Sheet{Filename: fileName, Sheet: "Sheet1"}).Scan(&deDupSieve); err != nil {
		return nil, err
	}

	var (
		baseFile = filepath.Base(fileName)
		index    = 1
		count    = 1
		files    []string
		ss       = strings.Split(baseFile, ".")
		ff       = filepath.Join(filepath.Dir(fileName), fmt.Sprintf("%v_%v.txt", ss[0], index))
	)

	for i, v := range deDupSieve {

		data := fmt.Sprintf("%v:%v:%v:%v:%v:%v:%v:%v:%v:%v:%v", v.Number, v.CarrierId, v.CarrierName, v.CarrierMnc,
			v.Validity, v.NumberType, v.CountryCode, v.CountryName,
			v.LastKnownEvent, v.DisconnectDate, v.ProvinceCode)

		deDupSieveR = append(deDupSieveR, data)
		if count >= npf && npf != 0 {
			files = append(files, ff)
			count = 1
			buff := &bytes.Buffer{}
			buff.WriteString(strings.Join(deDupSieveR, fmt.Sprintf("%c%c", 0x0D, 0x0A)))
			_ = os.WriteFile(ff, buff.Bytes(), 0644)
			deDupSieveR = deDupSieveR[:0]
			if i+1 >= len(deDupSieve) {
				break
			}
			index = index + 1
			ff = filepath.Join(filepath.Dir(fileName), fmt.Sprintf("%v_%v.txt", ss[0], index))
			continue
		}
		count = count + 1
	}

	if len(deDupSieveR) > 0 {
		buff := &bytes.Buffer{}
		buff.WriteString(strings.Join(deDupSieveR, fmt.Sprintf("%c%c", 0x0D, 0x0A)))
		_ = os.WriteFile(ff, buff.Bytes(), 0644)
		files = append(files, ff)
	}
	return files, nil
}

// 通用的excel 格式导出,带文件分割功能（不用考虑文件的格式）
func GeneralExcelFileSplitToExcel(fileName string, npf int) ([]string, error) {

	var (
		deDupSieve  []GeneralSieveSexExportType
		deDupSieveR []GeneralSieveSexExportType
	)

	if err := (excel.Sheet{Filename: fileName, Sheet: "Sheet1"}).Scan(&deDupSieve); err != nil {
		return nil, err
	}

	var (
		baseFile = filepath.Base(fileName)
		index    = 1
		count    = 1
		files    []string
		ss       = strings.Split(baseFile, ".")
		ff       = filepath.Join(filepath.Dir(fileName), fmt.Sprintf("%v_%v.xlsx", ss[0], index))
	)

	for i, v := range deDupSieve {

		deDupSieveR = append(deDupSieveR, v)
		if count >= npf && npf != 0 {
			files = append(files, ff)
			count = 1
			buff, _ := (&excel.Sheet{Sheet: "Sheet1"}).Export(&deDupSieveR)
			_ = os.WriteFile(ff, buff.Bytes(), 0644)
			deDupSieveR = deDupSieveR[:0]
			if i+1 >= len(deDupSieve) {
				break
			}
			index = index + 1
			ff = filepath.Join(filepath.Dir(fileName), fmt.Sprintf("%v_%v.xlsx", ss[0], index))
			continue
		}
		count = count + 1
	}

	if len(deDupSieveR) > 0 {
		buff, _ := (&excel.Sheet{Sheet: "Sheet1"}).Export(&deDupSieveR)
		_ = os.WriteFile(ff, buff.Bytes(), 0644)
		files = append(files, ff)
	}
	return files, nil
}

func SexExcelFileSplitToHtml(fileName string, npf int, args ...interface{}) ([]string, error) {

	var (
		deDupSieve  []SieveSexExportType
		deDupSieveR []SieveSexExportType
		exportKind  int
		tableHtml   string
		ageMap      = make(map[string]struct{})
	)
	if len(args) == 1 {
		exportKind = args[0].(int)
	}

	if len(args) == 2 {
		exportKind, tableHtml = args[0].(int), args[1].(string)

	}
	if len(args) == 3 {
		exportKind, tableHtml = args[0].(int), args[1].(string)
		if args[2].(string) != "" {
			s := strings.Split(args[2].(string), "-")
			if len(s) == 1 {
				ageMap[s[0]] = struct{}{}
			}
			if len(s) == 2 {
				for i := convert.Atoi(s[0]); i <= convert.Atoi(s[1]); i++ {
					ageMap[strconv.Itoa(i)] = struct{}{}
				}
			}
		}
	}

	if err := (excel.Sheet{Filename: fileName, Sheet: "Sheet1"}).Scan(&deDupSieve); err != nil {
		return nil, err
	}

	var (
		templ    = template.Must(template.New("").Parse(tableHtml))
		baseFile = filepath.Base(fileName)
		index    = 1
		count    = 1
		files    []string
		ss       = strings.Split(baseFile, ".")
		ff       = filepath.Join(filepath.Dir(fileName), fmt.Sprintf("%v_%v.html", ss[0], index))
	)

	fWriter, err := os.Create(ff)
	if err != nil {
		return nil, err
	}

	for i, v := range deDupSieve {

		if exportKind == 1 && v.Sex != "male" {
			continue
		}

		if exportKind == 2 && v.Sex != "female" {
			continue
		}

		if exportKind == 4 && (v.Sex != "female" && v.Sex != "male") {
			continue
		}

		if _, ok := ageMap[v.Age]; !ok && len(ageMap) > 0 && (exportKind == 1 || exportKind == 2 || exportKind == 4) {
			continue
		}

		deDupSieveR = append(deDupSieveR, v)
		if count >= npf && npf != 0 {
			files = append(files, ff)
			count = 1

			_ = templ.Execute(fWriter, deDupSieveR)
			deDupSieveR = deDupSieveR[:0]
			if i+1 >= len(deDupSieve) {
				break
			}
			index = index + 1
			ff = filepath.Join(filepath.Dir(fileName), fmt.Sprintf("%v_%v.html", ss[0], index))

			fWriter, _ = os.Create(ff)
			continue
		}
		count = count + 1
	}

	if len(deDupSieveR) > 0 {
		_ = templ.Execute(fWriter, deDupSieveR)

		_ = fWriter.Close()
		files = append(files, ff)
	}
	return files, nil
}

func FileSplitToXlsx(fileName string, npf int) ([]string, error) {

	f, err := os.Open(fileName)
	if err != nil {
		return nil, err
	}
	defer f.Close()
	reader := bufio.NewReader(f)

	var (
		baseFile = filepath.Base(fileName)
		index    = 1
		count    = 1
		files    []string
		ss       = strings.Split(baseFile, ".")
		ff       = filepath.Join(filepath.Dir(fileName), fmt.Sprintf("%v_%v.xlsx", ss[0], index))
	)

	excel := excelize.NewFile()
	rWriter, err := excel.NewStreamWriter("Sheet1")
	if err != nil {
		return nil, err
	}

	var hasExtraFile = true
	for {
		line, _, err := reader.ReadLine()
		if err == io.EOF {
			break
		}

		_ = rWriter.SetRow(fmt.Sprintf("A%v", count), []interface{}{fmt.Sprintf("%v%c%c", string(line), 0x0D, 0x0A)})
		if count >= npf {
			files = append(files, ff)
			err = rWriter.Flush()
			fmt.Println(err)
			err = excel.SaveAs(ff)
			fmt.Println(err)
			count = 1

			if reader.Buffered() <= 0 {
				hasExtraFile = false
				break
			}

			index = index + 1
			ff = filepath.Join(filepath.Dir(fileName), fmt.Sprintf("%v_%v.csv", ss[0], index))

			excel = excelize.NewFile()
			rWriter, err = excel.NewStreamWriter("Sheet1")

			fmt.Println(err)
			continue
		}
		count = count + 1
	}
	if hasExtraFile {
		files = append(files, ff)
		_ = rWriter.Flush()
		_ = excel.SaveAs(ff)
	}
	return files, nil
}

func NewBitMapFromFile(fileName string, args ...interface{}) (*roaring64.Bitmap, int, error) {

	var (
		bitArray       bool
		realTaskNumber int
		areaCode       string // 暂时只对文本文件做过滤
	)

	if len(args) == 1 {
		bitArray = args[0].(bool)
	}

	if len(args) == 2 {
		bitArray, areaCode = args[0].(bool), args[1].(string)
	}

	if b := PathExist(fileName); b {
		return nil, 0, errors.New("not file")
	}

	r := roaring64.New()
	f, err := os.Open(fileName)
	if err != nil {
		return nil, 0, err
	}

	defer f.Close()

	reader := bufio.NewReader(f)

	if bitArray {
		_, err = r.ReadFrom(f)
		if err != nil {
			return nil, 0, err
		}

		realTaskNumber = int(r.GetCardinality())
	} else {
		for {
			line, _, err := reader.ReadLine()
			if err == io.EOF {
				break
			}

			/*
				1. 过滤前后空格
				2. 过滤非手机号
				3. 暂时不支持带 "-" 的料子，如果后面要支持，不能用 strconv.ParseUint 过滤
			*/

			lineStr := strings.TrimSpace(string(line))
			x, err := strconv.ParseUint(lineStr, 10, 64)
			if err != nil {
				continue
			}

			realTaskNumber = realTaskNumber + 1
			if strings.Index(lineStr, areaCode) != 0 {
				continue
			}

			r.Add(x)
		}
	}

	return r, realTaskNumber, err
}

func ToFileFromBitMap(fileName string, ba *roaring64.Bitmap, args ...interface{}) error {

	var (
		bitArray     bool
		areaCode     string
		numberLength int
		extraLength  int
	)

	if len(args) == 1 {
		bitArray = args[0].(bool)
	}

	if len(args) == 2 {
		bitArray, areaCode = args[0].(bool), args[1].(string)
	}

	if len(args) == 3 {
		bitArray, areaCode = args[0].(bool), args[1].(string)
		numberLength = args[2].(int)
		extraLength = numberLength - len(areaCode)
	}

	f, err := os.Create(fileName)
	if err != nil {
		return err
	}
	defer f.Close()

	if bitArray {
		_, err = ba.WriteTo(f)
		if err != nil {
			return err
		}
		return nil
	}

	bufWriter := bufio.NewWriter(f)

	for _, v := range ba.ToArray() {

		vv := fmt.Sprintf("%0*d", extraLength, v)
		_, _ = bufWriter.WriteString(fmt.Sprintf("%v%v%c%c", areaCode, vv, 0x0D, 0x0A))
	}

	return bufWriter.Flush()
}

//func ToFileFromBitMap(fileName string, ba *roaring64.Bitmap) error {
//
//	f, err := os.Open(fileName)
//	if err != nil {
//		return err
//	}
//	defer f.Close()
//	_, err = ba.WriteTo(f)
//	if err != nil {
//		return err
//	}
//
//	return nil
//}

func CreateZipFile(zipPath string, fileList []string, ff func(fName string) string, fCallBack func(fName string)) error {

	if len(fileList) < 1 {
		return fmt.Errorf("file list is empty")
	}

	fz, err := os.Create(zipPath)
	if err != nil {
		return err
	}

	zw := zip.NewWriter(fz)

	defer func() {
		_ = zw.Close()
		_ = fz.Close()
		if fCallBack != nil {
			fCallBack(fz.Name())
		}
		//_ = os.Remove(fz.Name())
	}()

	for _, fileName := range fileList {
		fr, err := os.Open(ff(fileName))
		if err != nil {
			return err
		}

		stat, _ := fr.Stat()
		if stat.IsDir() {
			return errors.New("only support package for files")
		}

		// 写入文件的头信息
		var w io.Writer
		w, err = zw.Create(filepath.Base(fileName))

		if err != nil {
			return err
		}

		// 写入文件内容
		_, err = io.Copy(w, fr)
		if err != nil {
			return err
		}
	}
	return zw.Flush()
}
